package oop.ex2.commands;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import oop.ex2.exceptions.EmptyExpression;
import oop.ex2.exceptions.ExpectedAtomicExpression;
import oop.ex2.exceptions.ExpectedBinaryOperation;
import oop.ex2.exceptions.TerminatingStringNotFound;
import oop.ex2.exceptions.TokenNotRecognized;
import oop.ex2.exceptions.UnrecognizedVariable;
import oop.ex2.expression.ExpressionSolver;
import oop.ex2.expression.FunctionNode;
import oop.ex2.tokenizer.CustomBinaryAnalyzer;

/**
 * This implements the overload command. It expects an input of the form:
 * overload $$ FUNC
 * It parses the input and checks if it's in the right format. If so,
 * it creates a custom binary operator and adds it to it's expression solver.
 * @author daniel_i_l
 *
 */
public class OverloadCommand extends Command {
	private static final String COMMAND_PATTERN = "\\s*overload\\s";
	private static final String OP_PATTERN = "\\s*[^a-zA-Z0-9 \t\\(\\)\\[\\]\\{\\}@_=]{1,2}\\s\\s*";
	private static final String FUNCTION_PATTERN = "_?[A-Z]+\\s*";
	private static Pattern opPattern;
	private static Pattern commandPattern;
	private static Pattern functionPattern;
	
	/**
     * Construct an overload command object using a specific expression solver. This is the expression
     * solver that will be updated whenever a new operator is added.
     * @param expressionSolver The expression solver to use.
     */
	public OverloadCommand(ExpressionSolver expressionSolver) {
		super(expressionSolver);
		opPattern = Pattern.compile(OP_PATTERN);
		commandPattern = Pattern.compile(COMMAND_PATTERN);
		functionPattern = Pattern.compile(FUNCTION_PATTERN);
	}

	@Override
	public boolean executeCommand(String command) throws TokenNotRecognized,
			EmptyExpression, ExpectedAtomicExpression, ExpectedBinaryOperation,
			TerminatingStringNotFound, UnrecognizedVariable {
		
		//look for 'overload'
		Matcher commandMatcher = commandPattern.matcher(command);
		if (!commandMatcher.lookingAt())
			return false;
		
		command = command.substring(commandMatcher.end());
		
		//check for operator
		Matcher opMatcher = opPattern.matcher(command);
		if (!opMatcher.lookingAt())
			return false;
		
		String operator = opMatcher.group().trim();
		
		command = command.substring(opMatcher.end());
		
		//look for command function name
		Matcher functionMatcher = functionPattern.matcher(command);
		if ( (!functionMatcher.lookingAt()) || (!functionMatcher.group().equals(command)))
			return false;
		
		FunctionNode functionNode = _expressionSolver.getFunctionNode(functionMatcher.group());
		
		if (!functionNode.isBinaryFunction())
			return false;
		
		//remove any analyzers that have the same operator and add the new one
		_expressionSolver.removeBinaryOpAnalyzer(operator);
		operator = sanitizeOperator(operator);
		CustomBinaryAnalyzer newAnalyzer = new CustomBinaryAnalyzer(operator, functionNode);
		_expressionSolver.addBinaryOpAnalyzer(newAnalyzer);
		
		return true;
		
	}
	
	private String sanitizeOperator(String operator)
	{
		String output;

		output = operator.replaceAll("\\+", "\\\\+");
		output = output.replaceAll("\\$", "\\\\\\$");
		output = output.replaceAll("\\^", "\\\\^");
		output = output.replaceAll("\\*", "\\\\*");
		output = output.replaceAll("\\.", "\\\\.");

		return output;
	}

}
